﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml.Serialization;
using System.IO;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Formatters.Binary;

namespace DataPreperation
{
    class DBGenerator
    {
        GradedDB db;

        public DBGenerator()
        {
            db = new GradedDB();
        }

        // the main method - recieves a list of items, generates a GradedDB object, and serializing it to a binary file named "SerializedDB.bin"
        public void GenerateDB(List<IItem> items)
        {
            for (int i = 0; i < items.Count; i++) // adding every item to the db object
            {
                IItem item = items[i];
                Tuple<string, double, string> itemToAdd;
                itemToAdd = new Tuple<string, double, string>(item.GetID(), item.getTMScore(), item.GetText());
                db.items.Add(itemToAdd);

                for (int j = i + 1; j < items.Count; j++)  // adding the cosine similarities of the item to the db
                {
                    simDoc s1 = new simDoc(items[i].GetText(), items[i].GetID());
                    simDoc s2 = new simDoc(items[j].GetText(), items[j].GetID());
                    double similarity = CosineSimilarity(s1, s2);
                    ItemPair p = new ItemPair(items[i],items[j]);
                    db.CosSim.Add(p, similarity);
                }
            }
            
            // serializing and writing to file
            IFormatter formatter = new BinaryFormatter();
            Stream stream = new FileStream("SerializedDB.bin", FileMode.Create, FileAccess.Write, FileShare.None);
            formatter.Serialize(stream, db);
            stream.Close();


            //XmlSerializer writer = new XmlSerializer(db.GetType());
            //StreamWriter file = new StreamWriter("SerializedDB.xml");
            //writer.Serialize(file, db);
            //file.Close();

        }

        static double CosineSimilarity(simDoc a, simDoc b)
        {
            double ans = 0;
            List<string> miniLexicon = new List<string>(a.TF.Keys);
            foreach (string key in b.TF.Keys)
            {
                if (!miniLexicon.Contains(key))
                {
                    miniLexicon.Add(key);
                }
            }

            double[] vecA = new double[miniLexicon.Count];
            double[] vecB = new double[miniLexicon.Count];

            double ab = 0, aPow = 0, bPow = 0;
            for (int i = 0; i < miniLexicon.Count; i++)
            {
                vecA[i] = a.NumberOfOccurrences(miniLexicon[i]);
                vecB[i] = b.NumberOfOccurrences(miniLexicon[i]);
                ab += vecA[i] * vecB[i];
                aPow += Math.Pow(vecA[i], 2);
                bPow += Math.Pow(vecB[i], 2);
            }

            ans = ab / (Math.Sqrt(aPow) * Math.Sqrt(bPow));

            return ans;
        }

        static Dictionary<string, double> IDF(List<simDoc> dataset)
        {
            List<string> lexicon = new List<string>();
            Dictionary<string, double> idf = new Dictionary<string, double>();

            foreach (simDoc document in dataset)
            {
                string[] docArray = document.Text.Split(' ');
                foreach (string word in docArray)
                {
                    if (!lexicon.Contains(word))
                    {
                        lexicon.Add(word);
                    }
                }
            }

            double numOfOccurencesInDataset;
            foreach (string word in lexicon)
            {
                numOfOccurencesInDataset = 0;
                foreach (simDoc document in dataset)
                {
                    if (document.Contains(word))
                    {
                        numOfOccurencesInDataset++;
                    }
                }
                idf.Add(word, Math.Log(Math.Abs((double)dataset.Count / numOfOccurencesInDataset), 2));
            }

            return idf;
        }

        static double TFIDF(string word, simDoc document, Dictionary<string, double> idf)
        {
            double ans;
            if (!document.Contains(word))
            {
                ans = 0;
            }
            else
            {
                ans = document.TF[word] * idf[word];
            }
            return ans;
        }

    }

    static class IDMaker
    {
        static int _seed = 0;
        static public int GetUID()
        {
            return _seed++;
        }
    }

    class simDoc
    {
        int _id;
        string _name;
        string _text;
        int _maxFrequency;
        Dictionary<string, double> _tf = new Dictionary<string, double>();

        public simDoc(string text, string name)
        {
            _id = IDMaker.GetUID();
            _text = text.ToLower();
            _name = name;
            _maxFrequency = 0;
            string[] docArray = _text.Split(' ');
            foreach (string word in docArray)
            {
                if (_tf.ContainsKey(word))
                {
                    _tf[word]++;
                }
                else
                {
                    _tf[word] = 1;
                }

                if (_tf[word] > _maxFrequency)
                {
                    _maxFrequency = (int)_tf[word];
                }
            }

            List<string> keys = _tf.Keys.ToList();
            foreach (string key in keys)
            {
                _tf[key] /= _maxFrequency;
            }
        }

        public string Text
        {
            get { return _text; }
        }

        public string Name
        {
            get { return _name; }
        }
        int MaxFrequency
        {
            get { return _maxFrequency; }
        }

        public double NumberOfOccurrences(string word)
        {
            double ans;
            if (!_text.Split(' ').Contains(word))
            {
                return 0;
            }
            else
            {
                ans = 0;
                string[] strArray = _text.Split(' ');
                for (int i = 0; i < strArray.Length; i++)
                {
                    if (strArray[i].Equals(word))
                    {
                        ans++;
                    }
                }
            }
            return ans;
        }

        public Dictionary<string, double> TF
        {
            get { return _tf; }
        }

        public override string ToString()
        {
            string ans = _text + ": ";
            foreach (KeyValuePair<string, double> pair in _tf)
            {
                ans += Environment.NewLine + "--" + pair.Key + ": " + pair.Value;
            }
            return ans;
        }

        public Boolean Contains(string word)
        {
            if (_text.Split(' ').Contains(word))
            {
                return true;
            }
            else
            {
                return false;
            }
        }
    }
}
